package product

import (
	"context"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/common/utils/pointy"
	"mall/service/backend/common/errorx"
	"mall/service/backend/common/i18n"
	"mall/service/product/model"
	"mall/service/product/rpc/internal/svc"
	"mall/service/product/rpc/internal/utils/errorhandler"
	"mall/service/product/rpc/types/product"

	"github.com/zeromicro/go-zero/core/logx"
)

type BackendGetProductByIdLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewBackendGetProductByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BackendGetProductByIdLogic {
	return &BackendGetProductByIdLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *BackendGetProductByIdLogic) BackendGetProductById(in *product.ProductIdReq) (*product.ProductInfo, error) {
	if in.Id == 0 {
		return nil, errorx.NewInvalidArgumentError(i18n.ValidationError)
	}

	res, err := l.svcCtx.CurrentProduct.FindOne(l.ctx, in.Id)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	var content *string
	var contentFiltered *string
	var tracing *string
	var tracingFiltered *string
	if (in.WithContent != nil && *in.WithContent > 0) || (in.WithTracing != nil && *in.WithTracing > 0) {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductDetailColumns.ProductID] = map[string][]any{
			"equalTo": {in.Id},
		}
		list, _, err := l.svcCtx.CurrentProduct.GetDetailList(l.ctx, model.M{
			"pageSize": uint64(1),
			"orderBy":  model.ProductDetailColumns.DetailID + " DESC",
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			detail := list[0]
			content = pointy.GetPointer(detail.Content)
			contentFiltered = pointy.GetPointer(detail.ContentFiltered)
			if detail.Tracing.Valid {
				tracing = pointy.GetPointer(detail.Tracing.String)
			}
			if detail.TracingFiltered.Valid {
				tracingFiltered = pointy.GetPointer(detail.TracingFiltered.String)
			}
		}
	}

	var meta []*product.MetaInfo
	var pendingStatus int32
	var commentCount int64
	var sticky int32
	if in.WithMeta != nil && *in.WithMeta > 0 {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductMetumColumns.ProductID] = map[string][]any{
			"equalTo": {in.Id},
		}
		list, _, err := l.svcCtx.CurrentProduct.GetMeta(l.ctx, model.M{
			"orderBy": model.ProductMetumColumns.MetaID + " DESC",
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			keyMap := make(map[string]struct{})
			for _, v := range list {
				if len(v.MetaKey) > 0 && (v.MetaKey == "_thumbnail_id" || v.MetaKey == "_thumbnail_url") {
					continue
				}

				if len(v.MetaKey) > 0 && v.MetaValue.Valid {
					key := v.MetaKey
					val := v.MetaValue.String
					if _, ok := keyMap[key]; ok { // 防重复
						continue
					}

					keyMap[key] = struct{}{}

					switch key {
					case "_pending_status":
						if i, err := strconv.ParseInt(val, 10, 32); err == nil {
							pendingStatus = int32(i)
							meta = append(meta, &product.MetaInfo{
								Id:    &v.MetaID,
								Key:   pointy.GetPointer("pendingStatus"),
								Value: pointy.GetPointer(val),
							})
						}
					case "_photo_count":
						meta = append(meta, &product.MetaInfo{
							Id:    &v.MetaID,
							Key:   pointy.GetPointer("photoCount"),
							Value: pointy.GetPointer(val),
						})
					case "_view_count":
						meta = append(meta, &product.MetaInfo{
							Id:    &v.MetaID,
							Key:   pointy.GetPointer("viewCount"),
							Value: pointy.GetPointer(val),
						})
					case "_comment_count":
						if i, err := strconv.ParseInt(val, 10, 32); err == nil {
							commentCount = i
						}
					case "_forward_count":
						meta = append(meta, &product.MetaInfo{
							Id:    &v.MetaID,
							Key:   pointy.GetPointer("forwardCount"),
							Value: pointy.GetPointer(val),
						})
					case "_sticky":
						switch val {
						case "frontpage":
							sticky = globalkey.BlogStickyTypeFrontpage
						case "category":
							sticky = globalkey.BlogStickyTypeCategory
						case "tag":
							sticky = globalkey.BlogStickyTypeTag
						}
					case "_gem_price_min":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("gemPriceMin"),
							Value: pointy.GetPointer(val),
						})
					case "_gem_price_max":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("gemPriceMax"),
							Value: pointy.GetPointer(val),
						})
					case "_retail_price_min":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("retailPriceMin"),
							Value: pointy.GetPointer(val),
						})
					case "_retail_price_max":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("retailPriceMax"),
							Value: pointy.GetPointer(val),
						})
					case "_stock":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("stock"),
							Value: pointy.GetPointer(val),
						})
					case "_total_sale_count":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("saleCount"),
							Value: pointy.GetPointer(val),
						})
					case "_is_hot":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("isHot"),
							Value: pointy.GetPointer(val),
						})
					case "_sync":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("sync"),
							Value: pointy.GetPointer(val),
						})
					case "_sync_from":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("syncFrom"),
							Value: pointy.GetPointer(val),
						})
					case "_sell_point":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("sellPoint"),
							Value: pointy.GetPointer(val),
						})
					case "_stock_unit":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("stockUnit"),
							Value: pointy.GetPointer(val),
						})
					case "_show_stock":
						meta = append(meta, &product.MetaInfo{
							Key:   pointy.GetPointer("showStock"),
							Value: pointy.GetPointer(val),
						})
					case "_review_count", "_rating_count", "_pre_sale_count", "_upvote_count", "title_extra", "keywords_extra", "description_extra":
						//
					default:
						meta = append(meta, &product.MetaInfo{
							Id:    &v.MetaID,
							Key:   pointy.GetPointer(key),
							Value: pointy.GetPointer(val),
						})
					}
				}
			}
		}
	}
	status := int32(res.Status)
	var visibility uint32
	if status == globalkey.PostStatusPending {
		switch pendingStatus {
		case globalkey.PostStatusPrivate:
			visibility = 3
		case globalkey.PostStatusFuture:
			visibility = 2
		default:
			visibility = 1
		}
	} else {
		switch status {
		case globalkey.PostStatusPrivate:
			visibility = 3
		case globalkey.PostStatusFuture:
			visibility = 2
		default:
			visibility = 1
		}
	}

	var media []*product.MediaInfo
	if in.WithMedia != nil && *in.WithMedia > 0 {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductAttachmentRelationshipColumns.ObjectID] = map[string][]any{
			"equalTo": {in.Id},
		}
		filterMap[model.ProductAttachmentRelationshipColumns.ObjectType] = map[string][]any{
			"equalTo": {globalkey.AttachmentLogTypeProduct},
		}
		var orderBy string = model.ProductAttachmentRelationshipColumns.AttachmentThumbnail + " DESC, " +
			model.ProductAttachmentRelationshipColumns.AttachmentOrder
		list, _, err := l.svcCtx.CurrentProduct.GetMedia(l.ctx, model.M{
			"orderBy": orderBy,
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			for _, v := range list {
				media = append(media, &product.MediaInfo{
					Uid:       &v.AttachmentRelationshipID,
					Id:        pointy.GetPointer(strconv.FormatUint(v.AttachmentID, 10)),
					Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
					Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
					Uri:       &v.AttachmentSource,
					Url:       &v.AttachmentImageURL,
					AltText:   &v.AttachmentImageAlt,
					MetaData:  &v.AttachmentImageMeta,
				})
			}
		}
	}

	var categories []*product.TermInfo
	var tags []*product.TermInfo
	if (in.WithCategory != nil && *in.WithCategory > 0) || (in.WithTag != nil && *in.WithTag > 0) {
		subFilterMap := make(map[string]map[string][]any)
		subFilterMap[model.ProductTermRelationshipColumns.ObjectID] = map[string][]any{
			"equalTo": {in.Id},
		}
		subFilterMap[model.ProductTermRelationshipColumns.ObjectType] = map[string][]any{
			"equalTo": {globalkey.PostTypeProduct},
		}
		list, _, err := l.svcCtx.CurrentProductTerm.GetRelationshipList(l.ctx, model.M{
			"select": model.ProductTermRelationshipColumns.ObjectID + "," + model.ProductTermRelationshipColumns.TermID,
		}, -1, subFilterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			var tids []any
			for _, v := range list {
				tids = append(tids, v.TermID)
			}

			termFilterMap := make(map[string]map[string][]any)
			termFilterMap[model.ProductTermColumns.TermID] = map[string][]any{
				"in": tids,
			}
			terms, _, err := l.svcCtx.CurrentProductTerm.GetList(l.ctx, model.M{}, -1, termFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(terms) > 0 {
				for _, term := range terms {
					if term.Taxonomy == uint16(globalkey.TermTaxonomyCategory) { // 是分类
						if in.WithCategory != nil && *in.WithCategory > 0 {
							categories = append(categories, &product.TermInfo{
								Id:   &term.TermID,
								Name: &term.Name,
								Slug: &term.Slug,
							})
						}
					} else if term.Taxonomy == uint16(globalkey.TermTaxonomyProductTag) { // 是标签
						if in.WithTag != nil && *in.WithTag > 0 {
							tags = append(tags, &product.TermInfo{
								Id:   &term.TermID,
								Name: &term.Name,
								Slug: &term.Slug,
							})
						}
					}
				}
			}
		}
	}

	var entries []*product.EntryInfo
	if in.WithEntry != nil && *in.WithEntry > 0 {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductKnowledgeBaseRelationshipColumns.ProductID] = map[string][]any{
			"equalTo": {in.Id},
		}
		list, _, err := l.svcCtx.CurrentProduct.GetEntryList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			for _, v := range list {
				if v.ToEntryID > 0 {
					entries = append(entries, &product.EntryInfo{
						Id:   &v.ToEntryID,
						Key:  pointy.GetPointer(globalkey.ProductRelationNamePerfume),
						Name: &v.ToEntryName,
						Slug: &v.ToEntrySlug,
					})
				}
				if v.ToEntryGroupID > 0 {
					entries = append(entries, &product.EntryInfo{
						Id:   &v.ToEntryGroupID,
						Key:  pointy.GetPointer(globalkey.ProductRelationNameBrand),
						Name: &v.ToEntryGroupName,
						Slug: &v.ToEntryGroupSlug,
					})
				}
			}
		}
	}

	var attrs []*product.ProductAttrInfo
	var specs []*product.ProductAttrInfo
	if in.WithAttr != nil && *in.WithAttr > 0 {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductAttributeColumns.ProductID] = map[string][]any{
			"equalTo": {in.Id},
		}
		if in.WithSpec == nil || *in.WithSpec == 0 {
			filterMap[model.ProductAttributeColumns.IsVariationAttribute] = map[string][]any{
				"equalTo": {0},
			}
		}
		list, _, err := l.svcCtx.CurrentProduct.GetAttributeList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			var attrIds []any
			for _, v := range list {
				attrIds = append(attrIds, v.ProductAttributeID)
			}

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.ProductAttributeOptionColumns.AttributeID] = map[string][]any{
				"in": attrIds,
			}
			optList, _, err := l.svcCtx.CurrentProduct.GetAttributeOptionList(l.ctx, model.M{}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			attrOptMap := make(map[uint64][]*product.ProductAttrOptInfo)
			if len(optList) > 0 {
				for _, v := range optList {
					attrOptMap[v.AttributeID] = append(attrOptMap[v.AttributeID], &product.ProductAttrOptInfo{
						Uid:      &v.OptionID,
						AttrId:   &v.AttributeID,
						Id:       &v.TaxonomyOptionID,
						Status:   pointy.GetPointer(int32(v.Status)),
						Name:     &v.Name,
						Slug:     &v.Slug,
						MediaId:  pointy.GetPointer(strconv.FormatInt(int64(v.ThumbnailID), 10)),
						MediaUrl: &v.ThumbnailURL,
					})
				}
			}

			for _, v := range list {
				var opts []*product.ProductAttrOptInfo
				if os, ok := attrOptMap[v.ProductAttributeID]; ok {
					opts = os[:]
				}

				if v.IsVariationAttribute > 0 {
					specs = append(specs, &product.ProductAttrInfo{
						Uid:         &v.ProductAttributeID,
						ProductId:   &v.ProductID,
						TermId:      &v.TermID,
						Id:          &v.Taxonomy,
						IsVariation: pointy.GetPointer(uint32(v.IsVariationAttribute)),
						Name:        &v.Name,
						Slug:        &v.Slug,
						HasMedia:    pointy.GetPointer(uint32(v.HasImage)),
						Status:      pointy.GetPointer(int32(v.Status)),
						Options:     opts,
					})
				} else {
					attrs = append(attrs, &product.ProductAttrInfo{
						Uid:         &v.ProductAttributeID,
						ProductId:   &v.ProductID,
						TermId:      &v.TermID,
						Id:          &v.Taxonomy,
						IsVariation: pointy.GetPointer(uint32(v.IsVariationAttribute)),
						Name:        &v.Name,
						Slug:        &v.Slug,
						HasMedia:    pointy.GetPointer(uint32(v.HasImage)),
						Status:      pointy.GetPointer(int32(v.Status)),
						Options:     opts,
					})
				}
			}
		}
	}

	var skus []*product.ProductVariationInfo
	if in.WithSku != nil && *in.WithSku > 0 {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.ProductVariationColumns.ProductID] = map[string][]any{
			"equalTo": {in.Id},
		}
		list, _, err := l.svcCtx.CurrentProduct.GetVariationList(l.ctx, model.M{}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			var skuIds []any
			for _, v := range list {
				skuIds = append(skuIds, v.ProductVariationID)
			}

			skuMetaFilterMap := make(map[string]map[string][]any)
			skuMetaFilterMap[model.ProductVariationMetumColumns.ProductID] = map[string][]any{
				"in": skuIds,
			}
			skuMetaFilterMap[model.ProductVariationMetumColumns.MetaKey] = map[string][]any{
				"in": {
					"_thumbnail_id",
					"_thumbnail_url",
					"_sku_key",
					"_sku",
					"_stock",
					"_pre_sale_count", // 预定义的销量
				},
			}
			skuMetaList, _, err := l.svcCtx.CurrentProduct.GetVariationMeta(l.ctx, model.M{
				"orderBy": model.ProductVariationMetumColumns.MetaID,
			}, -1, skuMetaFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			skuMetaMap := make(map[uint64]*product.ProductVariationInfo)
			if len(skuMetaList) > 0 {
				for _, v := range skuMetaList {
					key := strings.TrimSpace(v.MetaKey)
					if len(key) > 0 && v.MetaValue.Valid {
						if _, ok := skuMetaMap[v.ProductID]; !ok {
							skuMetaMap[v.ProductID] = &product.ProductVariationInfo{}
						}
						val := v.MetaValue.String
						switch key {
						case "_thumbnail_id":
							skuMetaMap[v.ProductID].MediaId = pointy.GetPointer(val)
						case "_thumbnail_url":
							skuMetaMap[v.ProductID].MediaUrl = pointy.GetPointer(val)
						case "_sku_key": // 2  属性值ID
							skuMetaMap[v.ProductID].Key = pointy.GetPointer(val)
						case "_sku": // 1-净含量-Net volume:0-10ml-  属性及其值
							skuMetaMap[v.ProductID].Value = pointy.GetPointer(val)
						case "_stock":
							if i, err := strconv.ParseInt(val, 10, 32); err == nil {
								skuMetaMap[v.ProductID].Inventory = pointy.GetPointer(uint32(i))
							}
						case "_pre_sale_count":
							if i, err := strconv.ParseInt(val, 10, 32); err == nil {
								skuMetaMap[v.ProductID].SaleCount = pointy.GetPointer(uint32(i))
							}
						}
					}
				}
			}

			for _, v := range list {
				var mediaId *string
				var mediaUrl *string
				var inventory *uint32
				var saleCount *uint32
				var specs []*product.ProductAttrInfo
				if v, ok := skuMetaMap[v.ProductVariationID]; ok {
					mediaId = v.MediaId
					mediaUrl = v.MediaUrl
					inventory = v.Inventory
					saleCount = v.SaleCount
					specs = l.parseSpecs(v.Value, v.Key)
				}
				skus = append(skus, &product.ProductVariationInfo{
					Id:           &v.ProductVariationID,
					GemPrice:     pointy.GetPointer(moneyx.FormatGem(int64(v.GemPrice))),
					RetailPrice:  pointy.GetPointer(moneyx.FormatAmount(int64(v.RetailPrice))),
					CounterPrice: pointy.GetPointer(moneyx.FormatAmount(int64(v.CounterPrice))),
					Weight:       pointy.GetPointer(moneyx.FormatWeight(int64(v.Weight))),
					SkuCode:      &v.SkuCode,
					SkuType:      pointy.GetPointer(uint32(v.SkuType)),
					Status:       pointy.GetPointer(uint32(v.Status)),
					MediaId:      mediaId,
					MediaUrl:     mediaUrl,
					Inventory:    inventory,
					SaleCount:    saleCount,
					Specs:        specs,
				})
			}
		}
	}

	return &product.ProductInfo{
		Id:              &res.ID,
		Title:           &res.Title,
		Slug:            &res.Slug,
		ProductCode:     &res.ProductCode,
		Content:         content,
		ContentFiltered: contentFiltered,
		Tracing:         tracing,
		TracingFiltered: tracingFiltered,
		Excerpt:         &res.Excerpt,
		Status:          pointy.GetPointer(uint32(status)),
		Visibility:      pointy.GetPointer(uint32(visibility)), // Todo: 重构 临时用法：借用visibility传递publicTime的值
		Sort:            pointy.GetPointer(uint32(res.SortOrder)),
		Uri:             &res.GUID,
		AuthorId:        &res.ProductAuthorID,
		CategoryId:      &res.CategoryID,
		ParentId:        &res.ParentID,
		Sticky:          pointy.GetPointer(uint32(sticky)),
		VirtualType:     pointy.GetPointer(uint32(res.VirtualType)),
		CreatedAt:       pointy.GetPointer(modelx.FormatTime(res.ProductDate, "")),
		UpdatedAt:       pointy.GetPointer(modelx.FormatTime(res.ProductModifiedDate, "")),
		DeletedAt:       pointy.GetPointer(modelx.FormatNullDotTime(res.DeletedAt, "")),
		CommentCount:    pointy.GetPointer(uint32(commentCount)),
		Meta:            meta,
		Media:           media,
		Categories:      categories,
		Tags:            tags,
		Relations:       entries,
		Attrs:           attrs,
		Specs:           specs,
		Skus:            skus,
	}, nil
}

func (l *BackendGetProductByIdLogic) parseSpecs(sku *string, skuKey *string) (res []*product.ProductAttrInfo) {
	if sku == nil || skuKey == nil {
		return
	}

	uids := strings.Split(*skuKey, arrayValueSeparator) // 2,10
	if len(uids) > 0 {
		opts := strings.Split(*sku, arrayKeyValueSeparator) // 1-净含量-Net volume:0-10ml-;
		if len(opts) > 0 && len(opts) == len(uids) {
			for idx, opt := range opts {
				parts := strings.Split(opt, keyValueSeparator)
				if len(parts) == 2 {
					if uid, err := strconv.ParseInt(uids[idx], 10, 64); err == nil {
						specParts := strings.Split(parts[0], LSVSeparator)
						optParts := strings.Split(parts[1], LSVSeparator)
						if len(specParts) == 3 && len(optParts) == 3 {
							if specId, err := strconv.ParseInt(specParts[0], 10, 64); err == nil {
								if optId, err := strconv.ParseInt(optParts[0], 10, 64); err == nil {
									options := []*product.ProductAttrOptInfo{{
										Uid:   pointy.GetPointer(uint64(uid)),
										Id:    pointy.GetPointer(uint64(optId)),
										Label: pointy.GetPointer(optParts[1]),
										Slug:  pointy.GetPointer(optParts[2]),
									}}
									res = append(res, &product.ProductAttrInfo{
										Id:      pointy.GetPointer(uint64(specId)),
										Label:   pointy.GetPointer(specParts[1]),
										Slug:    pointy.GetPointer(specParts[2]),
										Options: options,
									})
								}
							}
						}
					}
				}
			}
		}
	}

	return
}
